home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIX 6.2 Applications 1996 May
/
SGI IRIX 6.2 Applications 1996 May.iso
/
dist
/
impr_dev.idb
/
usr
/
impressario
/
src
/
libspool
/
SLSysV.c.z
/
SLSysV.c
Wrap
C/C++ Source or Header
|
1996-05-06
|
70KB
|
2,458 lines
/**************************************************************************
* *
* Copyright (c) 1991 Silicon Graphics, Inc. *
* All Rights Reserved *
* *
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI *
* *
* The copyright notice above does not evidence any actual of intended *
* publication of such source code, and is an unpublished work by Silicon *
* Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is *
* the property of Silicon Graphics, Inc. Any use, duplication or *
* disclosure not specifically authorized by Silicon Graphics is strictly *
* prohibited. *
* *
* RESTRICTED RIGHTS LEGEND: *
* *
* Use, duplication or disclosure by the Government is subject to *
* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in *
* Technical Data and Computer Software clause at DFARS 52.227-7013, *
* and/or in similar or successor clauses in the FAR, DOD or NASA FAR *
* Supplement. Unpublished - rights reserved under the Copyright Laws of *
* the United States. Contractor is SILICON GRAPHICS, INC., 2011 N. *
* Shoreline Blvd., Mountain View, CA 94039-7311 *
**************************************************************************
*
* File: SLSysV.c
*
* Description: This file contains the System V spooling system specific
* handling functions.
*
**************************************************************************/
#ident "$Revision: 1.19 $"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "spoolI.h"
#ifndef sgi
#define PATH_MAX _POSIX_PATH_MAX
#endif
/* System V spooling directories and files */
#define SYSV_DEFAULT_FILE "/var/spool/lp/default"
#define SYSV_MEMBER_DIR "/var/spool/lp/member"
#define SYSV_CLASS_DIR "/var/spool/lp/class"
#define SYSV_INTERFACE_DIR "/var/spool/lp/interface"
#define SYSV_SPOOLER_FILE ".glprc"
#define SYSV_SETTINGS_DIR "/var/spool/lp/settings"
#define SYSV_DEFAULT_SETTINGS "defaultSettings"
/* System V rc file switches */
#define SYSV_COPY_SWITCH 'c'
#define SYSV_PRINTER_SWITCH 'd'
#define SYSV_MAIL_SWITCH 'm'
#define SYSV_MESS_SWITCH 'w'
#define SYSV_NUMCOPIES_SWITCH 'n'
#define SYSV_OPTIONS_SWITCH 'o'
#define SYSV_SUPPRESS_SWITCH 's'
#define SYSV_TITLE_SWITCH 't'
#define SYSV_MESS_OPTION "-w"
#define SYSV_OPTIONS_OPTION "-o"
#define SYSV_SUPPRESS_OPTION "-s"
/* System V misc values */
#define SYSV_LOGMAX 15 /* Max chars in a logname according to lp.h */
/* Interface file keyword parsing structure */
typedef struct {
char *key; /* Keyword in interface script */
short len; /* Number of characters in key */
char **pvar; /* Pointer to string var in printer struct */
short found; /* Indicates if keyword found */
} KeywordStruct;
static KeywordStruct keywords[] = {
{ "TYPE", 4, NULL, 0 },
{ "NAME", 4, NULL, 0 },
{ "HOSTNAME", 8, NULL, 0 },
{ "HOSTPRINTER", 11, NULL, 0 },
{ "NETTYPE", 7, NULL, 0 },
};
static int num_keywords = sizeof(keywords) / sizeof(KeywordStruct);
/* Union for spooler independent and spooler dependent settings structs */
typedef union {
SLSettingsStruct *generic; /* Spooler independent */
SLSysVSpoolerOptionsStruct *specific; /* Spooler specific */
} SettingsUnion;
#define SYSV_GENERIC 0
#define SYSV_SPECIFIC 1
/* Print queue */
static SLQueueStruct *sysv_queue; /* Job queue */
static int num_sysv_queue; /* Number of jobs in queue */
/* Local functions */
static int get_printer_info(const char*, SLPrinterStruct*);
static int get_class_member(const char*, char*);
static void get_class_info(const char*, SLPrinterStruct*, SLPrinterStruct*);
static void parse_interface(SLPrinterStruct*, FILE*);
static SLPrinterStruct *find_printer(char*, SLPrinterStruct*, int);
static char *get_def_pname(void);
static void parse_queue(SLQueueStruct*, char*);
static time_t parse_tstamp(const char*);
static uid_t getlpuid(void);
static void read_spooler_opts_file(SettingsUnion*, int);
static char* read_printer_opts_file(const char*);
static int write_spooler_opts_file(SLSysVSpoolerOptionsStruct*);
static int write_printer_opts_file(const char*, char*, int);
static void add_opts(char**, char*);
static int sgi_get_queue(const SLPrinterStruct *printer_info, int queue_type,
SLQueueStruct *queuep[], int *num_queuep);
static char *parse(char *string, char separator);
static int jobs_equal(const SLPrinterStruct *printer_info,
SLQueueStruct *q1, SLQueueStruct *q2);
static void sgi_parse_queue(SLQueueStruct *entry, char *line);
/**************************************************************************
*
* Function: _SLSysVFindSpooler
*
* Description: Determines whether the System V print scheduler is running.
*
* Parameters: none
*
* Return: 1 if the scheduler is running, 0 otherwise.
*
**************************************************************************/
int _SLSysVFindSpooler(void)
{
char cmd_buf[SL_BUFSIZ];
/*
* Run "lpstat -r". If the spooler is running, lpstat will print
* "scheduler is running". Grep for the "is running" part.
*/
(void)sprintf(cmd_buf, "%s -r", SL_CMD_LPSTAT);
if (_SLExec(NULL, cmd_buf, SL_FALSE, NULL) == 0
&& _SLspooler_nout
&& strstr(*_SLspooler_out_buf, "is running") != NULL) {
return 1;
}
return 0;
}
/**************************************************************************
*
* Function: _SLSysVGetPrinterList
*
* Description: Provides a list of the printers recognized by the
* System V spooling system. This function frees the old internal
* printer list and allocates a new one. Because of this any pointer
* the user may have from a previous call to this function is made
* invalid. If a user wishes to preserve the old list, he must
* copy it before calling this function.
*
* Parameters:
* printersp (O) - set to a list of available printers
* num_printersp (O) - number of available printers in list
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetPrinterList(SLPrinterStruct *printersp[], int *num_printersp)
{
DIR *dptr;
struct dirent *dir_entry;
char buf[SL_SML_BUFSIZ], *cptr;
SLPrinterStruct info, *printer;
static SLPrinterStruct *sysv_printers = NULL;
static int num_sysv_printers = 0;
/*
* Clear out and initialize the list of System V printers
*/
_SLInitPlist(&sysv_printers, &num_sysv_printers);
/*
* Read the printer member directory to find the known
* printers.
*/
if ((dptr = opendir(SYSV_MEMBER_DIR)) == NULL) {
*num_printersp = num_sysv_printers;
*printersp = sysv_printers;
return SL_NOERROR;
}
while ((dir_entry = readdir(dptr)) != NULL) {
/*
* Skip the . entries
*/
if (*dir_entry->d_name == '.')
continue;
/*
* Get the printer info. If the printer is valid, add it
* to the list of printers
*/
if (get_printer_info(dir_entry->d_name, &info) < 0)
continue;
_SLAddPrinter(&info, &sysv_printers, &num_sysv_printers);
}
(void)closedir(dptr);
/*
* Check the class directory for any printer classes. A
* printer class will be treated as another printer. The
* class name will be the local_name and the data for
* the printer will come from the printer that is the
* first entry in the class file
*/
if ((dptr = opendir(SYSV_CLASS_DIR)) != NULL) {
while ((dir_entry = readdir(dptr)) != NULL) {
if (*dir_entry->d_name == '.')
continue;
/*
* Read the first line in the class file to determine
* which printer's characteristics this class should have.
* If there are any problems with the class skip it.
*/
if (get_class_member(dir_entry->d_name, buf) < 0)
continue;
/*
* Now locate the printer specified in the class file
*/
if ((printer = find_printer(buf, sysv_printers, num_sysv_printers))
== NULL)
continue;
/*
* Add the printer class to the printer list
*/
get_class_info(dir_entry->d_name, printer, &info);
_SLAddPrinter(&info, &sysv_printers, &num_sysv_printers);
}
(void)closedir(dptr);
}
/*
* Finally, identify the default printer, if any
*/
if ((cptr = get_def_pname()) != NULL) {
if ((printer = find_printer(cptr, sysv_printers, num_sysv_printers))
!= NULL) {
printer->is_def = 1;
}
}
*num_printersp = num_sysv_printers;
*printersp = sysv_printers;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetPrinterInfo
*
* Description: Provides detailed information about the specified System
* V printer. Each time this function is called the old contents of
* the internal info structure become invalid. If the user wishes to
* preserve the old info, a copy must be made before calling this
* function.
*
* Parameters:
* printer (I) - printer whose info is wanted. Public function sets
* this to default printer name if user specified as
* NULL.
* printer_infop (O) - set to a printer information structure
* filled with information about the printer. Set
* to NULL if the specified printer does not exist.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred. It is considered an error specifying a printer
* that is not available to the spooler.
*
**************************************************************************/
int _SLSysVGetPrinterInfo(const char *printer, SLPrinterStruct **printer_infop)
{
char buf[SL_SML_BUFSIZ], *dptr;
SLPrinterStruct info;
static SLPrinterStruct pinfo;
/*
* First clear any storage that may have been allocated for the struct
*/
_SLInitPentry(&pinfo);
/*
* Next assume that this is a normal printer name and try to fill
* in information.
*/
if (get_printer_info(printer, &pinfo) == 0) {
*printer_infop = &pinfo;
}
/*
* If not a valid printer name try it as a classname
*/
else {
if (get_class_member(printer, buf) < 0)
RETURN_ERROR(SL_ERR_BAD_PRINTER_NAME);
if (get_printer_info(buf, &info) < 0)
RETURN_ERROR(SL_ERR_BAD_CLASS_MEMBER);
get_class_info(printer, &info, &pinfo);
*printer_infop = &pinfo;
}
/*
* See if this is the default printer
*/
if ((dptr = get_def_pname()) != NULL) {
if (!strcmp(dptr, pinfo.local_name))
pinfo.is_def = 1;
}
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetDefPrinterName
*
* Description: Returns the default printer name recognized by the
* System V spooling system.
*
* Parameters:
* pnamep (O) - default printer or NULL if none selected.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred. It is considered an error if there is no
* default printer registered.
*
**************************************************************************/
int _SLSysVGetDefPrinterName(char **pnamep)
{
if ((*pnamep = get_def_pname()) == NULL)
RETURN_ERROR(SL_ERR_NO_DEF_PRINTER);
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetPrinterSettings
*
* Description: Reads the spooler and printer option settings that have
* been saved.
*
* Spooler options are stored in ~/SYSV_SPOOLER_FILE. Printer
* specific options are stored in the directory defined by
* SYSV_SETTINGS_DIR/[printer name]. The file is first looked
* for under the username and if not found is then looked for
* under the name defined by SYSV_DEFAULT_SETTINGS.
*
* Parameters:
* printer (I) - printer whose settings are wanted. If NULL,
* the default printer is used. If the printer does
* exist, default settings will be returned.
* settingsp (O) - set to a printer settings structure
* filled with the currently saved settings.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetPrinterSettings(const char *printer,
SLSettingsStruct **settingsp)
{
static SLSettingsStruct settings;
SettingsUnion sunion;
char *optr;
/*
* First clear any storage associated with the old settings
* and initialize the structure to the default state.
*/
settings.copy = 0;
settings.mail = 0;
if (settings.title) {
free((char*)settings.title);
settings.title = NULL;
}
if (settings.options) {
free((char*)settings.options);
settings.options = NULL;
}
/*
* Read the spooler options file
*/
sunion.generic = &settings;
read_spooler_opts_file(&sunion, SYSV_GENERIC);
/*
* Read the printer option settings file
*/
if ((optr = read_printer_opts_file(printer)) != NULL) {
char *opt_string = (char*)malloc((strlen(optr) + 10) * sizeof(char));
(void)sprintf(opt_string, "-o\"%s\"", optr);
add_opts(&settings.options, opt_string);
free((char*)opt_string);
free((char*)optr);
}
/*
* Point the caller at the settings
*/
*settingsp = &settings;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetSpoolerOptions
*
* Description: Reads the spooler options settings that have
* been saved. The settings are returned in a System V
* dependent structure.
*
* Spooler options are stored in ~/SYSV_SPOOLER_FILE.
*
* Parameters:
* spooler_optsp (O) - set to a spooler settings structure
* filled with the currently saved spooler options
* or default values if no spooler options file
* is found.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetSpoolerOptions(SLSysVSpoolerOptionsStruct **spooler_optsp)
{
static SLSysVSpoolerOptionsStruct spooler_opts;
SettingsUnion sunion;
/*
* First clear any storage associated with the old settings
* and initialize the structure to the default state.
*/
spooler_opts.copy = 0;
spooler_opts.mail = 0;
spooler_opts.message = 0;
spooler_opts.suppress_id = 0;
if (spooler_opts.title) {
free((char*)spooler_opts.title);
spooler_opts.title = NULL;
}
/*
* Read the spooler options file
*/
sunion.specific = &spooler_opts;
read_spooler_opts_file(&sunion, SYSV_SPECIFIC);
/*
* Point the caller at the options
*/
*spooler_optsp = &spooler_opts;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetPrinterOptions
*
* Description: Reads the printer specific option settings that have
* been saved. The options are returned as a single string. This
* is equivalent to the string specified using the "-o" switch to
* lp.
*
* Printer specific options are stored in the directory defined by
* SYSV_SETTINGS_DIR/[printer name]. The file is first looked
* for under the username and if not found is then looked for
* under the name defined by SYSV_DEFAULT_SETTINGS.
*
* Parameters:
* printer (I) - printer whose options are wanted.
* printer_optsp (O) - set to point to s string of printer specific
* options or NULL if no options can be found.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetPrinterOptions(const char *printer, char **printer_optsp)
{
static char *printer_opts;
/*
* First clear any storage associated with the old options.
*/
if (printer_opts) {
free((char*)printer_opts);
printer_opts = NULL;
}
/*
* Read the printer specific options file
*/
printer_opts = read_printer_opts_file(printer);
/*
* Point the caller at the settings
*/
*printer_optsp = printer_opts;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVSaveSpoolerOptions
*
* Description: Saves the spooler option settings for the System V spooler.
*
* Spooler options are stored in ~/SYSV_SPOOLER_FILE.
*
* Parameters:
* spooler_opts (I) - System V specific settings to be saved.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVSaveSpoolerOptions(SLSysVSpoolerOptionsStruct *spooler_opts)
{
if (write_spooler_opts_file(spooler_opts) < 0)
RETURN_ERROR(SL_ERR_SAVE_OPTIONS);
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVSavePrinterOptions
*
* Description: Saves the printer specific option settings.
*
* Printer specific options are stored in the directory defined by
* SYSV_SETTINGS_DIR/[printer name]. If location is set to
* SL_SAVE_USER the file is written under the username. If location
* is SL_SAVE_DEFAULT, the file is written with the name defined
* by SYSV_DEFAULT_SETTINGS.
*
* Parameters:
* printer (I) - printer whose options are to be saved.
* printer_opts (I) - pritner specific options to be saved.
* location (I) - Save settings for this user (SL_SAVE_USER) or
* for all users (SL_SAVE_DEFAULT).
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVSavePrinterOptions(const char *printer,
char *printer_opts, int location)
{
/*
* Write the options file
*/
if (write_printer_opts_file(printer, printer_opts, location) < 0)
RETURN_ERROR(SL_ERR_SAVE_OPTIONS);
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVSubmitJob
*
* Description: Submits a job for printing using the System V spooler.
*
* Parameters:
* job_source (I) - union describing the print job source (eg.
* filename, fd, etc.)
* printer (I) - printer on which to print job. Public function
* sets this to the default printer name if user
* specified this as NULL.
* num_copies (I) - number of copies
* copy (I) - Copy flag. If 1 then file is copied to spooling dir. If
* 0 a link is created.
* mail (I) - Mail flag. If 1 then mail is sent on job print completion.
* If 0, no mail is sent.
* title (I) - title to appear on banner page.
* options (I) - string of spooling system/printer specific options.
* Set to NULL if none.
* job_idp (O) - print job ID
*
* Return: 0 if no error. -1 and SLerrno set if error.
*
**************************************************************************/
int _SLSysVSubmitJob(SLJobSourceUnion *job_source, const char *printer,
int num_copies, int copy, int mail,
const char *title, const char *options,
char **job_idp)
{
char cmd_buf[SL_BUFSIZ], buf[SL_BUFSIZ];
char *id_ptr;
char *t_ptr;
char *b_ptr;
/*
* Start filling the command buffer with parameters. Start
* with the printing command and the destination printer. Command
* will looks something like:
* /usr/bin/lp -d5L -c -t"a \' test" /etc/passwd 2>&1
*/
(void)sprintf(cmd_buf, "%s -d%s", SL_CMD_LP, printer);
/* Number of copies */
if (num_copies > 1) {
(void)sprintf(buf, " -n%d", num_copies);
(void)strcat(cmd_buf, buf);
}
/* Copy file to spool dir */
if (copy)
(void)strcat(cmd_buf, " -c");
/* Mail sent on completion */
if (mail)
(void)strcat(cmd_buf, " -m");
/* Banner page title -- handle a char at a time to handle case of
* characters that the shell will interpret
*/
if (title) {
for (t_ptr = (char *)title, b_ptr = buf; *t_ptr; t_ptr++) {
if ( !isalnum((int) *t_ptr) && !isspace((int) *t_ptr) ) {
*b_ptr++ = '\\'; /* Escape these */
}
*b_ptr++ = *t_ptr;
if ((b_ptr - buf) == SL_BUFSIZ-10) break; /* Stay in bounds */
}
*b_ptr = '\0';
/* (void)sprintf(buf, " -t'%s'", title); */
(void)strcat(cmd_buf, " -t\"");
(void)strcat(cmd_buf, buf);
(void)strcat(cmd_buf, "\"");
}
/* Printer/Spooler specific options */
if (options) {
(void)sprintf(buf, " %s", options);
(void)strcat(cmd_buf, buf);
}
/* Add the file(s) to print, if appropriate */
if (job_source->type == SL_JOB_FILENAME) {
(void)sprintf(buf, " %s", job_source->filename_job.filename);
(void)strcat(cmd_buf, buf);
}
/* Lastly add the sh stderr redirection */
(void)sprintf(buf, " %s", SL_SH_REDIRECT);
(void)strcat(cmd_buf, buf);
/*
* Issue the command and if successful parse the output
* buffer for the job ID
*/
if (_SLExec(job_source, cmd_buf, SL_FALSE, NULL) != 0) {
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
if (_SLspooler_nout) {
char *loc;
(void)sprintf(cmd_buf, "%s-", printer);
if ((id_ptr = strstr(_SLspooler_out_buf[_SLspooler_nout-1], cmd_buf))
!= NULL) {
(void)strtok_r(id_ptr, " ", &loc);
}
*job_idp = id_ptr;
} else
*job_idp = NULL;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVCancelJob
*
* Description: Sends a cancel print job request to the System V spooling
* system. Note that there is no confirmation that a print job has
* actually been canceled.
*
* Parameters:
* job_id (I) - print job ID(s) of the job(s) that are to be canceled.
* printer (I) - not used. Printer is determined from job ID.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
/* ARGSUSED */
int _SLSysVCancelJob(const char *job_id, const char *printer)
{
char cmd_buf[SL_BUFSIZ];
/*
* Fill the command buffer with parameters
* cancel + job ID(s) + sh output redirection
*/
(void)sprintf(cmd_buf, "%s %s %s",
SL_CMD_CANCEL,
job_id,
SL_SH_REDIRECT);
/*
* Issue the command
*/
if (_SLExec(NULL, cmd_buf, SL_FALSE, NULL) != 0)
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetSpoolerState
*
* Description: Returns the state of the specified spooling function for
* the System V spooling system.
*
* Parameters:
* printer (I) - printer whose spooler state to query.
* function (I) - spooling function whose state to query (one of
* SL_PRINTING or SL_QUEUEING).
* statep (O) - state of the spooling function (one of SL_ENABLED or
* SL_DISABLED).
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetSpoolerState(const char *printer, int function, int *statep)
{
char cmd_buf[SL_BUFSIZ];
/*
* Fill the command buffer with parameters
* lpstat + printing | queueing + printer + sh output redirection
*/
(void)sprintf(cmd_buf, "%s -%c%s %s",
SL_CMD_LPSTAT,
(function == SL_PRINTING) ? 'p': 'a',
printer,
SL_SH_REDIRECT);
/*
* Issue the command
*/
if (_SLExec(NULL, cmd_buf, SL_FALSE, NULL) != 0)
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
/*
* Parse out the state information
*/
if (_SLspooler_nout) {
if (function == SL_PRINTING) {
if (strstr(*_SLspooler_out_buf, "disabled") != NULL)
*statep = SL_DISABLED;
else if (strstr(*_SLspooler_out_buf, "enabled") != NULL)
*statep = SL_ENABLED;
else {
_SLspooler_exit = 1; /* Stupid System V returned 0 */
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
} else { /* Queueing */
if (strstr(*_SLspooler_out_buf, "not accepting") != NULL)
*statep = SL_DISABLED;
else if (strstr(*_SLspooler_out_buf, "accepting") != NULL)
*statep = SL_ENABLED;
else {
_SLspooler_exit = 1; /* Stupid System V returned 0 */
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
}
} else
RETURN_ERROR(SL_ERR_NO_STATE);
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVSetSpoolerState
*
* Description: Sets the state of the specified spooling function for the
* System V spooling system.
*
* Parameters:
* printer (I) - printer whose spooler state to set.
* function (I) - spooling function whose state to set (one of
* SL_PRINTING or SL_QUEUEING).
* state (I) - state to set for the spooling function (one of
* SL_ENABLED or SL_DISABLED).
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVSetSpoolerState(const char *printer, int function, int state)
{
char cmd_buf[SL_BUFSIZ], buf[SL_BUFSIZ];
uid_t my_id;
/*
* Start filling the command buffer with parameters. Note
* that lp requires root or lp permission for enale/disable
* and for accept/reject. The System V spooling system in SGI
* Releases 4.0.1 and earlier allowed anyone to enable/disable.
* This has now been changed. Note also that for disable/enable
* I test your euid and for accept/reject I test uid. This is what
* the actual lp programs will test so I need to do the same so
* that you are not likely to pass my test and fail their's.
*/
if (function == SL_PRINTING) {
if ((my_id = geteuid()) != 0 && my_id != getlpuid())
RETURN_ERROR(SL_ERR_PRIVILEGE);
(void)strcpy(cmd_buf, (state == SL_ENABLED) ?
SL_CMD_ENABLE: SL_CMD_DISABLE);
} else { /* Queueing */
if ((my_id = getuid()) != 0 && my_id != getlpuid())
RETURN_ERROR(SL_ERR_PRIVILEGE);
(void)strcpy(cmd_buf, (state == SL_ENABLED) ?
SL_CMD_ACCEPT: SL_CMD_REJECT);
}
/* Add the printer and sh output redirection */
(void)sprintf(buf, " %s %s", printer, SL_SH_REDIRECT);
(void)strcat(cmd_buf, buf);
/*
* Issue the command
*/
if (_SLExec(NULL, cmd_buf, SL_FALSE, NULL) != 0)
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
/*
* Determine whether we really succeeded. System V is terrible
* at sending back error return codes if a command failed.
* The heuristic we use is if the first word on the first line
* of the returned string is the command name itself, we have failed.
* We worry about this only for disable/enable.
*/
if (_SLspooler_nout && function == SL_PRINTING) {
(void)sprintf(cmd_buf, "%s", (state == SL_ENABLED) ?
"enabled": "disabled");
if (strstr(*_SLspooler_out_buf, cmd_buf) == NULL) {
_SLspooler_exit = 1; /* Stupid System V returned 0 */
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
}
return SL_NOERROR;
}
/**************************************************************************
*
* Function: _SLSysVGetQueue
*
* Description: Returns the job queue for the specified printer under
* the System V spooling system.
*
* Parameters:
* printer_info (I) - printer structure. Cannot be NULL.
* queue_type (I) - type of print queue to obtain (ie. local, remote
* or merged). The value of this parameter is
* ignored for local printers.
* queuep (O) - print queue entries.
* num_queuep (O) - number of entries in the queue.
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
int _SLSysVGetQueue(const SLPrinterStruct *printer_info, int queue_type,
SLQueueStruct *queuep[], int *num_queuep)
{
char cmd_buf[SL_BUFSIZ];
int i, start_ind, timeout;
SLQueueStruct *qentry, *qptr, temp_entry;
/*
* Clear out and initialize the System V queue
*/
_SLInitQueue(&sysv_queue, &num_sysv_queue);
/*
* If the new stuff is installed, use it.
*/
if (access("/usr/lib/liblp.so", F_OK) == 0) {
return sgi_get_queue(printer_info, queue_type, queuep, num_queuep);
}
/*
* First get the remote queue if this is a remote printer and we
* have not specified local queue explicitly
*/
if (printer_info->is_networked && queue_type != SL_QUEUE_LOCAL) {
/*
* Form the remote command
*/
(void)sprintf(cmd_buf,
"%s %s -n -l lp '%s -c \"%s %s %s -o%s %s\"'",
SL_CMD_RSH, printer_info->remote_host,
SL_CMD_SH_NAME, SL_LANG, SL_MSGFORMAT, SL_CMD_LPSTAT,
printer_info->remote_name, SL_SH_REDIRECT);
/*
* Issue the remote command. Notice that we are issuing the
* command with a timeout. This is because we go over the
* net with rsh to get the remote queue. All kinds of nasty
* things can happen to trip us up, so we make sure that
* no matter what happens we will return from this call.
*/
if ((_SLExec(NULL, cmd_buf, SL_TRUE, &timeout) != 0) ||
(timeout == SL_TRUE))
RETURN_ERROR(SL_ERR_REMOTE);
if (_SLspooler_nout) {
(void)sprintf(cmd_buf, "%s:", SL_CMD_LPSTAT_NAME);
if (strstr(*_SLspooler_out_buf, cmd_buf)) {
_SLspooler_exit = 1; /* System V forgets */
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
}
/*
* Parse the command output to form the remote portion
* of the queue
*/
for (i = 0; i < _SLspooler_nout; i++) {
qentry = _SLAddQueue(&sysv_queue, &num_sysv_queue);
parse_queue(qentry, _SLspooler_out_buf[i]);
qentry->is_local = 0;
}
}
/*
* Now get the local queue if not networked or if want merged or
* local queue for remote printer.
*/
if (!printer_info->is_networked || queue_type != SL_QUEUE_REMOTE) {
(void)sprintf(cmd_buf, "%s -o%s %s", SL_CMD_LPSTAT,
printer_info->local_name, SL_SH_REDIRECT);
if (_SLExec(NULL, cmd_buf, SL_FALSE, NULL) != 0)
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
if (_SLspooler_nout) {
(void)sprintf(cmd_buf, "%s:", SL_CMD_LPSTAT_NAME);
if (strstr(*_SLspooler_out_buf, cmd_buf)) {
_SLspooler_exit = 1; /* System V forgets */
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
}
}
/*
* Merge the local queue with the remote queue, if any
* A merge is done by matching the first job on the local queue
* to the last job on the remote queue that has the same owner and
* same size. This is really piss poor, but given the general brain
* death in the architecture of these spooling systems, it is the
* best we can do.
*/
start_ind = 0;
if (printer_info->is_networked && queue_type == SL_QUEUE_MERGED) {
if (_SLspooler_nout) {
/*
* Parse the first entry in the local queue
*/
parse_queue(&temp_entry, *_SLspooler_out_buf);
temp_entry.is_local = 1;
/*
* Compare it against the remote queue starting with the
* last entry
*/
for (i = 0, qptr = &sysv_queue[num_sysv_queue-1];
i < num_sysv_queue; i++, qptr--) {
/*
* Two entries are considered identical if the owner and
* size are the same.
*/
if ((qptr->username && temp_entry.username) &&
(!strcmp(qptr->username, temp_entry.username)) &&
(qptr->size == temp_entry.size)) {
/*
* Free the remote entry and replace it with the
* local entry
*/
_SLFreeQueueEntry(qptr);
*qptr = temp_entry;
start_ind = 1;
break;
}
}
/*
* If first local entry not found on remote queue
* free the storage for the first local entry
*/
if (start_ind == 0)
_SLFreeQueueEntry(&temp_entry);
}
}
/*
* Parse the remaining command output to form the local
* queue portion of the queue
*/
if (!printer_info->is_networked || queue_type != SL_QUEUE_REMOTE) {
for (i = start_ind; i < _SLspooler_nout; i++) {
qentry = _SLAddQueue(&sysv_queue, &num_sysv_queue);
parse_queue(qentry, _SLspooler_out_buf[i]);
qentry->is_local = 1;
}
}
*num_queuep = num_sysv_queue;
*queuep = sysv_queue;
return SL_NOERROR;
}
#ifdef _SL_FASTPATH
/**************************************************************************
*
* Function: _SLSysVSupportsFastJob
*
* Description:
* Find out whether a printer supports fast print jobs
*
* Parameters:
* printer printer to find out if supports fast jobs
*
* Return: non-zero if fast job supported, 0 otherwise
*
**************************************************************************/
int _SLSysVSupportsFastJob(const char *printer)
{
int answer = SL_FALSE;
char line[SL_SML_BUFSIZ], model[SL_SML_BUFSIZ], *pr;
FILE *fp;
SLPrinterStruct *p;
static char *level2 = "LEVEL=2";
if (!printer) {
if (SLGetDefPrinterName(&pr) < 0) {
return SL_FALSE;
}
printer = pr;
}
if (SLGetPrinterInfo(printer, &p) < 0) {
return SL_FALSE;
}
if (p->dev) {
(void)sprintf(model, "%s/%s", SYSV_INTERFACE_DIR, printer);
fp = fopen(model, "r");
if (fp) {
while (fgets(line, sizeof(line), fp) != NULL) {
if (strstr(line, level2)) {
answer = SL_TRUE;
break;
}
}
(void)fclose(fp);
}
} else {
/*
* BLECH!!!!!
*/
(void)sprintf(line,
"/usr/bsd/rsh %s -n -l lp grep %s %s/%s %s",
p->remote_host, level2, SYSV_INTERFACE_DIR,
p->remote_name, SL_SH_REDIRECT);
answer = _SLExec(NULL, line, SL_TRUE, NULL);
}
return answer;
}
#endif /* _SL_FASTPATH */
/**************************************************************************
*
* Function: _SLSysVFindUserName
*
* Description: Determines the user name in the same manner as the System V
* spooling system. This code is taken from getname.c in
* usr/src/cmd/lp.
*
* Parameters: none
*
* Return: Pointer to user name. The storage for username is static and
* the returned value should be copied if it is not going to be
* used immediately after the call.
*
**************************************************************************/
char* _SLSysVFindUserName(void)
{
uid_t uid;
struct passwd *p;
static char logname[SYSV_LOGMAX + 1];
char *l;
uid = getuid();
setpwent();
l = getenv("LOGNAME");
if ((l == NULL) ||
#ifdef sgi
/* Let the administrator override the user's name to make
remote printing work better. */
((p = getpwnam(l)) == NULL || p->pw_uid != uid) &&
!(uid == 0 || (((p = getpwnam("lp")) != NULL) &&
p->pw_uid == uid)))
#else
(p = getpwnam(l)) == NULL || p -> pw_uid != uid)
#endif
if ((p = getpwuid(uid)) != NULL)
l = p->pw_name;
else
l = NULL;
if (l != NULL) {
(void)strncpy(logname, l, SYSV_LOGMAX);
logname[SYSV_LOGMAX] = '\0';
}
else
(void)sprintf(logname, "%ld", uid);
endpwent();
return logname;
}
/*
==========================================================================
LOCAL FUNCTIONS
==========================================================================
*/
/**************************************************************************
*
* Function: get_printer_info
*
* Description: Fills the specified print information structure with
* detailed information about the speicified printer.
*
* Parameters:
* pname (I) - printer name
* info (O) - printer info structure to be filled
*
* Return: 0 if the printer is valid or -1 if the printer is not
* valid.
*
**************************************************************************/
static int get_printer_info(const char *pname, SLPrinterStruct *info)
{
FILE *member_fptr, *inter_fptr;
char buf[SL_SML_BUFSIZ];
/*
* Attempt to open the member and interface files.
* If we have trouble indicate that the printer is invalid
*/
(void)sprintf(buf, "%s/%s", SYSV_MEMBER_DIR, pname);
if ((member_fptr = fopen(buf, "r")) == NULL)
return -1;
(void)sprintf(buf, "%s/%s", SYSV_INTERFACE_DIR, pname);
if ((inter_fptr = fopen(buf, "r")) == NULL) {
(void)fclose(member_fptr);
return -1;
}
/*
* Read the member file to determine what device the printer
* is connected to and whether it is networked
*/
if (fgets(buf, SL_SML_BUFSIZ, member_fptr) == NULL) {
(void)fclose(member_fptr);
(void)fclose(inter_fptr);
return -1;
}
buf[strlen(buf) - 1] = '\0';
info->local_name = strdup(SL_CHAR_CAST(pname));
if (strcmp(buf, "/dev/null")) {
info->dev = strdup(buf);
info->is_networked = 0;
} else {
info->dev = NULL;
info->is_networked = 1;
}
info->is_def = 0;
info->is_class = 0;
/*
* Next parse the interface script files for each printer to
* determine more details about the printers
*/
parse_interface(info, inter_fptr);
/*
* Close the open files
*/
(void)fclose(member_fptr);
(void)fclose(inter_fptr);
return 0;
}
/**************************************************************************
*
* Function: get_class_member
*
* Description: Provides the first member printer name from the specified
* printer class.
*
* Parameters:
* cname (I) - printer class name
* pname (O) - first member printer name. Should be dimensioned to
* SL_SML_BUFSIZ.
*
* Return: 0 if class is valid, -1 if not a valid class name.
*
**************************************************************************/
static int get_class_member(const char *cname, char *pname)
{
FILE *class_fptr;
char buf[SL_SML_BUFSIZ];
(void)sprintf(buf, "%s/%s", SYSV_CLASS_DIR, cname);
if ((class_fptr = fopen(buf, "r")) == NULL)
return -1;
if (fgets(pname, SL_SML_BUFSIZ, class_fptr) == NULL)
return -1;
(void)fclose(class_fptr);
pname[strlen(pname) - 1] = '\0';
return 0;
}
/**************************************************************************
*
* Function: get_class_info
*
* Description: Copies the printer class information from the specified
* printer information structure.
*
* Parameters:
* cname (I) - printer class name
* pinfo (I) - member printer information structure
* cinfo (O) - class information copied from pinfo
*
* Return: none
*
**************************************************************************/
static void get_class_info(const char *cname, SLPrinterStruct *pinfo,
SLPrinterStruct *cinfo)
{
cinfo->local_name = strdup(SL_CHAR_CAST(cname));
cinfo->formal_name = (pinfo->formal_name) ?
strdup(pinfo->formal_name): NULL;
cinfo->type = (pinfo->type) ?
strdup(pinfo->type): NULL;
cinfo->is_networked = pinfo->is_networked;
cinfo->is_def = 0;
cinfo->is_class = 1;
cinfo->remote_host = (pinfo->remote_host) ?
strdup(pinfo->remote_host): NULL;
cinfo->remote_name = (pinfo->remote_name) ?
strdup(pinfo->remote_name): NULL;
cinfo->dev = (pinfo->dev) ? strdup(pinfo->dev): NULL;
}
/**************************************************************************
*
* Function: parse_interface
*
* Description: Parses the specified printer's interface script file
* to determine information about the printer. The keywords should
* appear in the interface script with the following syntax:
*
* [whitespace]keyword[whitespace]=[whitespace]["]value["]
*
* where items in brackets are optional.
*
* Parameters:
* printer (I) - printer structure
* fptr (I) - pointer to the opened interface script file
*
* Return: none
*
**************************************************************************/
static void parse_interface(SLPrinterStruct *printer, FILE *fptr)
{
char buf[SL_SML_BUFSIZ], *sptr, *kptr;
register int i, len;
/*
* Initialize all info strings
*/
printer->type = NULL;
printer->formal_name = NULL;
printer->remote_host = NULL;
printer->remote_name = NULL;
printer->network_type = NULL;
keywords[0].pvar = &printer->type;
keywords[1].pvar = &printer->formal_name;
keywords[2].pvar = &printer->remote_host;
keywords[3].pvar = &printer->remote_name;
keywords[4].pvar = &printer->network_type;
/*
* Mark all keywords as not found
*/
for (i = 0; i < num_keywords; i++)
keywords[i].found = 0;
/*
* Parse the interface script
*/
while (fgets(buf, SL_SML_BUFSIZ, fptr)) {
/*
* Strip off trailing newline and any " character at the end
* We also handle the case where the '"' is followed by
* whitespace before the newline.
*/
len = strlen(buf);
buf[--len] = '\0';
for (i = len - 1; i >= 0; i--) {
if (buf[i] == ' ')
continue;
else if (buf[i] == '"') {
buf[i] = '\0';
break;
} else
break;
}
/*
* Skip any leading white space
*/
if ((sptr = _SLSkipSpace(buf)) == NULL)
continue;
/*
* Skip comment lines
*/
if (*sptr == '#')
continue;
/*
* Look for a keyword
*/
for (i = 0; i < num_keywords; i++) {
if (keywords[i].found)
continue;
if ((kptr = strstr(sptr, keywords[i].key)) == NULL)
continue;
if (kptr != buf && isalpha(kptr[-1]))
continue;
if ((kptr = _SLSkipSpace(kptr + keywords[i].len)) == NULL)
continue;
if (*kptr != '=')
continue;
if ((kptr = _SLSkipSpace(kptr + 1)) == NULL)
continue;
if (*kptr == '"')
kptr++;
*keywords[i].pvar = strdup(kptr);
keywords[i].found = 1;
break;
}
/*
* If all the needed info has been obtained stop reading
* the script file
*/
if (printer->type && printer->formal_name) {
if (printer->is_networked) {
if (printer->remote_host && printer->remote_name)
break;
} else
break;
}
}
/*
* oldsgi is the backwards compatible interface.
*/
if (printer->is_networked && !printer->network_type) {
printer->network_type = strdup("oldsgi");
}
/*
* Complete incomplete info items
*/
if (!printer->type)
printer->type = strdup("Unknown");
if (!printer->formal_name)
printer->formal_name = strdup(printer->type);
}
/**************************************************************************
*
* Function: find_printer
*
* Description: Performs a linear search of the printer list looking
* for the specified printer. The key is the local_name.
*
* Parameters:
* pname (I) - printer name to search for.
* plist (I) - list of available printers
* num_plist (I) - number of printers in the list
*
* Return: Returns a pointer to the printer structure if the printer was
* found in the printer list. Otherwise, NULL is returned.
*
**************************************************************************/
static SLPrinterStruct *find_printer(char *pname, SLPrinterStruct *plist,
int num_plist)
{
SLPrinterStruct *ptr = NULL;
register int i;
for (i = 0 ; i < num_plist; i++) {
if (!strcmp(plist[i].local_name, pname)) {
ptr = &plist[i];
break;
}
}
return ptr;
}
/**************************************************************************
*
* Function: get_def_pname
*
* Description: Gets the default printer name if any.
*
* Parameters: none
*
* Return: Returns a the default printer name or NULL if no default
* printer is registered.
*
**************************************************************************/
static char *get_def_pname(void)
{
FILE *fptr;
char *cptr = NULL;
static char buf[SL_SML_BUFSIZ];
/*
* Get the LPDEST environment variable setting, if any
* otherwise read the system default file for a default printer,
* if such a file exists
*/
if ((cptr = getenv("LPDEST")) == NULL) {
if ((fptr = fopen(SYSV_DEFAULT_FILE, "r")) != NULL) {
if ((cptr = fgets(buf, SL_SML_BUFSIZ, fptr)) != NULL)
buf[strlen(buf) - 1] = '\0';
(void)fclose(fptr);
}
}
/*
* If no def printer make sure pointer is NULL
*/
if (cptr && *cptr == '\0')
cptr = NULL;
return cptr;
}
/**************************************************************************
*
* Function: parse_queue
*
* Description: Parses an lpstat queue output string and places the
* appropriate info in the specified queue structure.
*
* Parameters:
* entry (O) - queue structure to be filled
* line (I) - lpstat output line to parse
*
* Return: none
*
**************************************************************************/
static void parse_queue(SLQueueStruct *entry, char *line)
{
char *sptr, *dline, *tstamp = NULL;
char *loc;
/*
* Initialize all fields in case not everything gets filled
*/
entry->job_id = entry->username = NULL;
entry->size = entry->time_stamp = 0;
entry->title = NULL;
dline = strdup(line);
if ((sptr = strtok_r(dline, " ", &loc)) != NULL) {
entry->job_id = strdup(sptr);
if ((sptr = strtok_r(NULL, " ", &loc)) != NULL) {
entry->username = strdup(sptr);
if ((sptr = strtok_r(NULL, " ", &loc)) != NULL) {
entry->size = atoi(sptr);
if ((tstamp = strtok_r(NULL, " ", &loc)) != NULL)
tstamp[strlen(tstamp)] = ' ';
}
}
}
entry->time_stamp = parse_tstamp(tstamp);
free((char*)dline);
}
/**************************************************************************
*
* Function: parse_tstamp
*
* Description: Takes a standard time string as returned by ctime and
* parses it into a valid tm struct to send to mktime. We do this
* so that when we get a time string from say lpstat for a queue
* entry we can put it in our struct as a time_t number. Having the
* time stamp represented as a number if easier to work with when
* comparing times.
*
* Parameters:
* time_stamp (I) - The ctime format time stamp string. The function
* works with whatever it is given as long as it follows
* the ctime format. For fields that are empty the
* functions tries to put in "reasonable" defaults.
* Leaving to much out will lead to unreasonable
* results.
*
* Return: The time in seconds since the epoch. This is the value that
* would be reported by time(2).
*
**************************************************************************/
#define TIME_SIZE 26
static time_t parse_tstamp(const char *time_stamp)
{
static const char *days = "SunMonTueWedThuFriSat";
static const char *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
char buf[TIME_SIZE], *sptr, *str, *jtime = NULL;
char *loc;
time_t tval;
struct tm *tmp, cur_tm, new_tm;
/*
* First check that we have anything to parse
*/
if (!time_stamp || *time_stamp == '\0')
return (time_t)0;
(void)strncpy(buf, time_stamp, TIME_SIZE);
buf[TIME_SIZE - 1] = '\0';
if ((str = strtok_r(buf, " ", &loc)) == NULL)
return (time_t)0;
/*
* Get the epoch time and the current time. The epoch is
* 00:00:00 GMT Jan 1, 1970. We initialize the new time value
* to the epoch and use the current time for daylight savings time
* info and the current year.
*/
tval = (time_t)0;
tmp = localtime(&tval);
new_tm = *tmp;
tval = time((time_t*)NULL);
tmp = localtime(&tval);
cur_tm = *tmp;
new_tm.tm_isdst = cur_tm.tm_isdst;
/*
* If the time stamp starts with a day name, ignore it
*/
if ((sptr = strstr(days, str)) != NULL)
if ((str = strtok_r(NULL, " ", &loc)) == NULL)
return (time_t)0;
/*
* Parse out the month, day and year. The time
* will be parse later
*/
if ((sptr = strstr(months, str)) != NULL) {
new_tm.tm_mon = (int)((sptr - months) / 3);
if ((str = strtok_r(NULL, " ", &loc)) != NULL) {
new_tm.tm_mday = atoi(str);
if ((jtime = strtok_r(NULL, " ", &loc)) != NULL) {
if ((str = strtok_r(NULL, " ", &loc)) != NULL) {
if ((new_tm.tm_year = atoi(str)) <= 0)
new_tm.tm_year = cur_tm.tm_year;
if (new_tm.tm_year > 1900)
new_tm.tm_year -= 1900;
} else {
new_tm.tm_year = cur_tm.tm_year;
}
}
}
}
/*
* Parse the time string
*/
if (jtime && *jtime) {
if ((str = strtok_r(jtime, ":", &loc)) != NULL) {
new_tm.tm_hour = atoi(str);
if ((str = strtok_r(NULL, ":", &loc)) != NULL) {
new_tm.tm_min = atoi(str);
if ((str = strtok_r(NULL, ":", &loc)) != NULL) {
new_tm.tm_sec = atoi(str);
}
}
}
}
/*
* Send back the time value. If it is negative simply set it to
* the epoch since it is invalid anyway
*/
if ((tval = mktime(&new_tm)) < 0)
tval = 0;
return tval;
}
/**************************************************************************
*
* Function: getlpuid
*
* Description: Returns the uid of the lp account. We do this by stating
* the lp spooling system directory, /var/spool/lp.
*
* Parameters: none
*
* Return: UID of the lp account.
*
**************************************************************************/
static uid_t getlpuid(void)
{
struct stat sbuf;
if (stat(SL_LP_DIR, &sbuf) < 0)
sbuf.st_uid = ~getuid();
return sbuf.st_uid;
}
/**************************************************************************
*
* Function: read_spooler_opts_file
*
* Description: Reads the spoooler options file ~/SYSV_SPOOLER_FILE and
* sets the settings structure fields accordingly.
*
* Parameters:
* sunion (O) - settings structure union to fill
* format (I) - token to identify which type of structure is
* to be filled: spooler specific or generic.
*
* Return: none
*
**************************************************************************/
static void read_spooler_opts_file(SettingsUnion *sunion, int format)
{
FILE *fptr;
char fname[PATH_MAX], *sptr, *ptr;
struct passwd *pw;
char *buffer, *bptr, *loc, *title;
struct stat sbuf;
register int len;
/*
* Open rc file if we can
*/
if ((pw = getpwuid(getuid())) == NULL)
return;
(void)sprintf(fname, "%s/%s", pw->pw_dir, SYSV_SPOOLER_FILE);
if ((fptr = fopen(fname, "r")) == NULL)
return;
/*
* Lock the file
*/
(void)flock(fileno(fptr), LOCK_SH);
/*
* Stat the file to determine its size and allocate a buffer to contain
* the entire file. The file will usually be very small so this is
* an efficient way to deal with things.
*/
if ((fstat(fileno(fptr), &sbuf) < 0) || !sbuf.st_size) {
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return;
}
if ((buffer = (char*)malloc(sbuf.st_size + 5)) == NULL) {
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return;
}
/*
* Read the file, make sure it is NULL terminated, unlock and
* close the file
*/
(void)fread(buffer, sbuf.st_size, 1, fptr);
buffer[sbuf.st_size] = '\0';
if ((sptr = strrchr(buffer, '\n')) != NULL)
*sptr = '\0';
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
/*
* Parse the buffer
*/
bptr = buffer;
while((sptr = strtok_r(bptr, "-", &loc)) != NULL) {
bptr = NULL;
for (len = strlen(sptr) - 1;
(len >= 0) && (sptr[len] == ' ' || sptr[len] == '\t'); len--)
sptr[len] = '\0';
switch(*sptr) {
case SYSV_COPY_SWITCH:
if (format == SYSV_GENERIC)
sunion->generic->copy = 1;
else
sunion->specific->copy = 1;
break;
case SYSV_PRINTER_SWITCH: /* Silently ignore this switch */
break;
case SYSV_MAIL_SWITCH:
if (format == SYSV_GENERIC)
sunion->generic->mail = 1;
else
sunion->specific->mail = 1;
break;
case SYSV_MESS_SWITCH:
if (format == SYSV_GENERIC)
add_opts(&sunion->generic->options, SYSV_MESS_OPTION);
else
sunion->specific->message = 1;
break;
case SYSV_NUMCOPIES_SWITCH: /* Silently ignore this switch */
break;
case SYSV_SUPPRESS_SWITCH:
if (format == SYSV_GENERIC)
add_opts(&sunion->generic->options, SYSV_SUPPRESS_OPTION);
else
sunion->specific->suppress_id = 1;
break;
case SYSV_TITLE_SWITCH:
title = "";
if (sptr[1] == '"')
title = strdup(&sptr[2]);
else
title = strdup(&sptr[1]);
if ((ptr = strrchr(title, '"')) != NULL)
*ptr = '\0';
if (*title == '\0')
break;
if (format == SYSV_GENERIC)
sunion->generic->title = title;
else
sunion->specific->title = title;
break;
default:
break;
}
}
/*
* Clean up and indicate that we read a file
*/
free((char*)buffer);
}
/**************************************************************************
*
* Function: write_spooler_opts_file
*
* Description: Writes the spooler options file ~/SYSV_SPOOLER_FILE.
*
* Parameters:
* spooler_opts (I) - options to write
*
* Return: 0 if successful, -1 if not.
*
**************************************************************************/
static int write_spooler_opts_file(SLSysVSpoolerOptionsStruct *spooler_opts)
{
int fd;
FILE *fptr;
char fname[PATH_MAX];
struct passwd *pw;
/*
* Open rc file if we can. We use low level IO since we want to open
* the file read/write, no truncation and create it if it does not exist.
* we truncate the file after we lock it.
*/
if ((pw = getpwuid(getuid())) == NULL)
return -1;
(void)sprintf(fname, "%s/%s", pw->pw_dir, SYSV_SPOOLER_FILE);
if ((fd = open(fname, O_RDWR | O_CREAT, 0666)) < 0)
return -1;
/*
* Lock the file and then truncate it.
*/
(void)flock(fd, LOCK_EX);
(void)ftruncate(fd, 0);
fptr = fdopen(fd, "r+");
/*
* Write the file. Note that we do not write the number of copies.
* The number of copies would cause more harm than good if it were
* placed in the rc file. Accidently setting 500 copies could have
* interesting results for future print jobs. We also do not write
* the printer name since this would raise confusion between the
* system default printer and the glprc printer.
*/
if (spooler_opts->copy)
(void)fprintf(fptr, "-%c ", SYSV_COPY_SWITCH);
if (spooler_opts->mail)
(void)fprintf(fptr, "-%c ", SYSV_MAIL_SWITCH);
if (spooler_opts->message)
(void)fprintf(fptr, "-%c ", SYSV_MESS_SWITCH);
if (spooler_opts->suppress_id)
(void)fprintf(fptr, "-%c ", SYSV_SUPPRESS_SWITCH);
if (spooler_opts->title) {
(void)fprintf(fptr, "-%c\"%s\" ", SYSV_TITLE_SWITCH,
spooler_opts->title);
}
(void)fprintf(fptr, "\n");
/*
* Set permissions on the file,
* unlock and close it
*/
(void)fflush(fptr);
(void)fchmod(fileno(fptr), 00644);
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return 0;
}
/**************************************************************************
*
* Function: read_printer_opts_file
*
* Description: Reads the printer specific options settings file for the
* specified printer.
*
* Printer specific options are stored in the directory defined by
* SYSV_SETTINGS_DIR/[printer name]. The file is first looked
* for under the username and if not found is then looked for
* under the name defined by SYSV_DEFAULT_SETTINGS.
*
* Parameters:
* pname (I) - name of printer whose settings file is to be read.
*
* Return: Pointer to the settings option string or NULL if no settings
* could be read for whatever reason. It is the caller's responsibility
* to free the storage for the string returned by this function.
*
**************************************************************************/
static char* read_printer_opts_file(const char *pname)
{
char *buffer;
struct stat sbuf;
FILE *fptr;
char fname[PATH_MAX], *sptr;
struct passwd *pw;
/*
* Look for the option file first by username then by default name.
*/
pw = getpwuid(getuid());
if (pw)
(void)sprintf(fname, "%s/%s/%s", SYSV_SETTINGS_DIR, pname,
pw->pw_name);
if (pw == NULL || (fptr = fopen(fname, "r")) == NULL) {
(void)sprintf(fname, "%s/%s/%s", SYSV_SETTINGS_DIR, pname,
SYSV_DEFAULT_SETTINGS);
if ((fptr = fopen(fname, "r")) == NULL)
return NULL;
}
/*
* Lock the file
*/
(void)flock(fileno(fptr), LOCK_SH);
/*
* Stat the file to determine its size and allocate a buffer to contain
* the entire file. The file will usually be very small so this is
* an efficient way to deal with things.
*/
if ((fstat(fileno(fptr), &sbuf) < 0) || !sbuf.st_size) {
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return NULL;
}
if ((buffer = (char*)malloc(sbuf.st_size + 5)) == NULL) {
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return NULL;
}
/*
* Read the settings file, make sure it is NULL terminated and
* close the file
*/
(void)fread(buffer, sbuf.st_size, 1, fptr);
buffer[sbuf.st_size] = '\0';
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
/*
* Break the settings file into two separate sections by scanning for
* \n characters and inserting \0. The settings of interest are on
* the first line. Ignore any additional lines.
*/
if ((sptr = strchr(buffer, '\n')) != NULL)
*sptr = '\0';
return buffer;
}
/**************************************************************************
*
* Function: write_printer_opts_file
*
* Description: Writes the printer specific options file for the
* specified printer.
*
* Printer specific options are stored in the directory defined by
* SYSV_SETTINGS_DIR/[printer name]. If location is set to
* SL_SAVE_USER the file is written under the username. If location
* is SL_SAVE_DEFAULT, the file is written with the name defined
* by SYSV_DEFAULT_SETTINGS.
*
* Parameters:
* pname (I) - name of printer whose options file is to be written.
* printer_opts (I) - printer options string
* location (I) - what form of options to write: local or global.
*
* Return: 0 if successful, -1 if not.
*
**************************************************************************/
static int write_printer_opts_file(const char *pname,
char *printer_opts, int location)
{
int fd;
FILE *fptr;
char fname[PATH_MAX];
struct passwd *pw;
/*
* Create the options file pathname
*/
if (location == SL_SAVE_DEFAULT) {
/*
* Verify that we can do this operation then form
* the pathname.
*/
if ((pw = getpwuid(geteuid())) == NULL)
return -1;
if (strcmp(pw->pw_name, "root") && strcmp(pw->pw_name, "lp"))
return -1;
(void)sprintf(fname, "%s/%s/%s", SYSV_SETTINGS_DIR, pname,
SYSV_DEFAULT_SETTINGS);
} else {
if ((pw = getpwuid(getuid())) == NULL)
return -1;
(void)sprintf(fname, "%s/%s/%s", SYSV_SETTINGS_DIR, pname, pw->pw_name);
}
/*
* Create the file. We use low level IO since we want to open
* the file read/write, no truncation and create it if it does not exist.
* we truncate the file after we lock it.
*/
if ((fd = open(fname, O_RDWR | O_CREAT, 0666)) < 0)
return -1;
/*
* Lock and truncate the file
*/
(void)flock(fd, LOCK_EX);
(void)ftruncate(fd, 0);
fptr = fdopen(fd, "r+");
/*
* Write the printer specific options
*/
if (printer_opts)
(void)fprintf(fptr, "%s\n", printer_opts);
else
(void)fprintf(fptr, "\n");
/*
* Set permissions on the file,
* unlock and close it
*/
(void)fflush(fptr);
(void)fchmod(fileno(fptr), 00644);
(void)flock(fileno(fptr), LOCK_UN);
(void)fclose(fptr);
return 0;
}
/**************************************************************************
*
* Function: add_opts
*
* Description: Appends the specified option string to the specified
* string.
*
* Parameters:
* opt_stringp (I) - string of options
* new_opt (I) - option to append to string
*
* Return: none
*
**************************************************************************/
static void add_opts(char **opt_stringp, char *new_opt)
{
if (*opt_stringp) {
*opt_stringp = (char*)realloc(*opt_stringp,
(strlen(*opt_stringp) + strlen(new_opt) + 5) *
sizeof(char));
(void)strcat(*opt_stringp, " ");
} else {
*opt_stringp = (char*)malloc((strlen(new_opt) + 5) * sizeof(char));
*opt_stringp[0] = '\0';
}
(void)strcat(*opt_stringp, new_opt);
}
/**************************************************************************
*
* Function: sgi_get_queue
*
* Description:
* Returns the job queue for the specified printer under the
* System V spooling system + SGI enhancements. The SGI
* enhancements consist of some new arguments to lpstat which
* were implemented just for us:
*
* lpstat -lprinter gives us printer's local queue
* lpstat -nprinter gives us printer's remote queue
* lpstat -bprinter gives us both queues, with the remote queue
* first and a blank line in between.
*
* The format of the output for all of these options is nice and
* easy to parse:
*
* job-id,username,size,title,timestamp
*
* Parameters:
* printer_info (I) - printer structure. Cannot be NULL.
* queue_type (I) - type of print queue to obtain (ie. local, remote
* or merged). The value of this parameter is
* ignored for local printers.
* queuep (O) - print queue entries.
* num_queuep (O) - number of entries in the queue.
*
*
* Return: 0 is execution succeeded. -1 and SLerrno is set if an execution
* error has occurred.
*
**************************************************************************/
static int sgi_get_queue(const SLPrinterStruct *printer_info, int queue_type,
SLQueueStruct *queuep[], int *num_queuep)
{
char cmdBuf[SL_BUFSIZ], letter;
int i, local, merge, timeout, nentries;
SLQueueStruct *qentry, *qptr, temp_entry;
/*
* Figure out which lpstat option to use
*
* The local flag is set to 1 if the first jobs we see will be
* local, 0 otherwise.
*/
if (!printer_info->is_networked) {
letter = 'l';
local = 1;
} else {
switch (queue_type) {
case SL_QUEUE_LOCAL:
letter = 'l';
local = 1;
break;
case SL_QUEUE_REMOTE:
letter = 'n';
local = 0;
break;
default:
case SL_QUEUE_MERGED:
letter = 'b';
local = 0;
break;
}
}
/*
* Run lpstat
*/
(void)sprintf(cmdBuf, "%s -%c%s %s", SL_CMD_LPSTAT, letter,
printer_info->local_name, SL_SH_REDIRECT);
if (_SLExec(NULL, cmdBuf, SL_FALSE, &timeout) != 0
|| timeout == SL_TRUE) {
RETURN_ERROR(SL_ERR_SPOOLER_ERROR);
}
/*
* Parse lpstat's output
*/
merge = 0;
for (i = 0; i < _SLspooler_nout; i++) {
/*
* If we see a blank line, then it's time to switch from
* parsing remote entries to parsing local enties. We treat
* the first local entry specially; if we find a job in the
* remote queue with the same user and size, we replace that
* job with the local job on the theory that they're really
* the same job.
*/
if (strlen(_SLspooler_out_buf[i]) == 0) {
merge = 1;
continue;
}
if (merge) {
merge = 0;
local = 1;
sgi_parse_queue(&temp_entry, _SLspooler_out_buf[i]);
temp_entry.is_local = 1;
for (nentries = num_sysv_queue,
qptr = sysv_queue + num_sysv_queue - 1;
nentries; nentries--, qptr--) {
if (jobs_equal(printer_info, qptr, &temp_entry)) {
_SLFreeQueueEntry(qptr);
*qptr = temp_entry;
break;
}
}
if (!nentries) {
qentry = _SLAddQueue(&sysv_queue, &num_sysv_queue);
*qentry = temp_entry;
}
continue;
}
qentry = _SLAddQueue(&sysv_queue, &num_sysv_queue);
sgi_parse_queue(qentry, _SLspooler_out_buf[i]);
qentry->is_local = local;
}
*num_queuep = num_sysv_queue;
*queuep = sysv_queue;
return SL_NOERROR;
}
/**************************************************************************
*
* Function: jobs_equal
*
* Description:
* Determine whether two job structures actually refer to the
* same job.
*
* Parameters:
* printer_info (I) the printer that these jobs refer to. Used
* to find out the network type.
* q1 (I) one job to compare
* q2 (I) the other job to compare
*
* Returns:
* 1 if jobs are the same, 0 if they're not.
*
**************************************************************************/
static int jobs_equal(const SLPrinterStruct *printer_info,
SLQueueStruct *q1, SLQueueStruct *q2)
{
int s1, s2;
char *pc;
/*
* We can be pretty sure that a job in our remote queue
* corresponds to a BSD remote job if their sequence numbers are
* the same, mod 1000. The mod 1000 is because BSD uses 3 digit
* sequence numbers, while System V uses 4 digit numbers.
*
* We check the user names even in this case as a sanity check.
* BSD jobs have sequence numbers from the machines from which
* they were submitted, so duplications are unlikely but possible.
*/
if (printer_info->network_type
&& strcmp(printer_info->network_type, "bsd") == 0) {
pc = strchr(q1->job_id, '-');
if (pc) {
s1 = atoi(pc + 1) % 1000;
pc = strchr(q2->job_id, '-');
if (pc) {
s2 = atoi(pc + 1) % 1000;
if (s1 == s2 &&
(!q1->username || !q2->username
|| strcmp(q1->username, q2->username) == 0)) {
return 1;
}
}
}
}
/*
* This is the old logic, the same logic you see in _SLGetQueue.
*
* This logic does NOT work for BSD queues, because we (by
* default) convert the data to PostScript. Thus, the job sizes
* will not be the same.
*/
return q1->size == q2->size
&& q1->username && q2->username
&& strcmp(q1->username, q2->username) == 0;
}
/**************************************************************************
*
* static char *parse(char *string, char separator)
*
* Description:
* This is a lot like strtok, except that the separator can
* consist of only one character (instead of a string), and if
* two separators are encountered in a row, NULL is returned.
*
* Note that this means that parse can return NULL for a token
* when a subsequent call will not return NULL.
*
* This is for parsing the output of lpstat -{lnb},
* which can have NULL tokens in it.
*
* Parameters:
* string string to parse
* separator char that occurs between tokens
*
* Returns:
* next token; can be NULL
*
**************************************************************************/
static char *parse(char *string, char separator)
{
static char *str;
char *sep, *rv;
/*
* Reset string if it's not NULL, just like in strtok.
*/
if (string) {
str = string;
}
/*
* Here's where we check for two separators in a row, and return
* NULL if we find them. Since we advance str past the separator
* when we return a token, if str is still pointing at a separator
* than there must have been two of them in a row.
*/
if (*str == separator) {
str++;
return NULL;
} else if (*str == '\0') {
return NULL;
}
rv = str;
sep = strchr(str, separator);
if (sep) {
*sep = '\0';
str = sep + 1; /* Advance str to next token for next call */
}
return rv;
}
/**************************************************************************
*
* Function: sgi_parse_queue
*
* Description:
* Parses "lpstat -l", "lpstat -n", or "lpstat -b" output into an
* SLQueueStruct.
*
* Parameters:
* entry (O) - queue structure to be filled
* line (I) - lpstat output line to parse
*
**************************************************************************/
static void sgi_parse_queue(SLQueueStruct *entry, char *line)
{
char *dline, *pc, *tstamp;
entry->job_id = NULL;
entry->username = NULL;
entry->title = NULL;
entry->size = 0;
entry->time_stamp = 0;
tstamp = NULL;
dline = strdup(line);
pc = parse(dline, ';');
if (pc) {
entry->job_id = strdup(pc);
}
pc = parse(NULL, ';');
if (pc) {
entry->username = strdup(pc);
}
pc = parse(NULL, ';');
if (pc) {
entry->size = atoi(pc);
}
pc = parse(NULL, ';');
if (pc) {
entry->title = strdup(pc);
}
pc = parse(NULL, ';');
if (pc) {
tstamp = pc;
}
entry->time_stamp = parse_tstamp(tstamp);
free(dline);
}